#include "stdafx.h"

#include "hwincomobject.h"
#include "hwinio.h"

namespace harlinn
{
    namespace windows
    {
        ULONG ComObjectBase::InternalRelease( )
        {
            long res = InterlockedIncrement(&referenceCount);
            if(!res)
            {
                delete this;
            }
            return res;
        }


        HWIN_EXPORT HRESULT ComStream::Read(void *pv,ULONG cb,ULONG *pcbRead)
        {
            HRESULT result = S_OK;
            try
            {
                if((!pv)||(!pcbRead))
                {
                    return STG_E_INVALIDPOINTER;
                }
                auto streamBase = GetStreamBase();
                long long bytesRead = streamBase->Read(pv,cb);
                *pcbRead = ULONG(bytesRead);
                if(*pcbRead < cb)
                {
                    result = S_FALSE;
                }
            }
            catch(const std::exception& exception)
            {
                result = HRESULTFromException(exception);
            }
            catch(...)
            {
                result = E_FAIL;
            }
            return result;
        }
        HWIN_EXPORT HRESULT ComStream::Write(const void *pv,ULONG cb,ULONG *pcbWritten)
        {
            HRESULT result = S_OK;
            try
            {
                if(!pv)
                {
                    return STG_E_INVALIDPOINTER;
                }
                auto streamBase = GetStreamBase();
                long long bytesWritten = streamBase->Write(pv,cb);
                if(pcbWritten)
                {
                    *pcbWritten = ULONG(bytesWritten);
                }
                if(bytesWritten < cb)
                {
                    result = STG_E_CANTSAVE;
                }
            }
            catch(const std::exception& exception)
            {
                result = HRESULTFromException(exception);
            }
            catch(...)
            {
                result = STG_E_CANTSAVE;
            }
            return result;
        }
        HWIN_EXPORT HRESULT ComStream::Seek(LARGE_INTEGER dlibMove,DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
        {
            HRESULT result = S_OK;
            try
            {
                SeekOrigin seekOrigin = (SeekOrigin)dwOrigin;
                auto streamBase = GetStreamBase();
                long long newPosition = streamBase->Seek(dlibMove.QuadPart,seekOrigin);
                if(plibNewPosition)
                {
                    plibNewPosition->QuadPart = newPosition;
                }
            }
            catch(const std::exception& exception)
            {
                result = HRESULTFromException(exception);
            }
            catch(...)
            {
                result = E_FAIL;
            }
            return result;
        }
        HWIN_EXPORT HRESULT ComStream::SetSize(ULARGE_INTEGER libNewSize)
        {
            HRESULT result = S_OK;
            try
            {
                long long newSize = (long long)libNewSize.QuadPart;
                auto streamBase = GetStreamBase();
                streamBase->SetSize(newSize);
                long long actualNewSize = streamBase->Size();
                if(newSize != actualNewSize)
                {
                    result = STG_E_INVALIDFUNCTION;
                }
                
            }
            catch(const std::exception& exception)
            {
                result = HRESULTFromException(exception);
            }
            catch(...)
            {
                result = E_FAIL;
            }
            return result;
        }

        HWIN_EXPORT HRESULT ComStream::CopyTo( IStream *pstm,ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
        {
            HRESULT result = S_OK;
            try
            {
                if(!pstm)
                {
                    return STG_E_INVALIDPOINTER;
                }
                auto streamBase = GetStreamBase();

                unsigned long long totalBytesRead = 0;
                unsigned long long totalBytesWritten = 0;
                unsigned long long bytesRead = 0;
                unsigned long bytesWritten = 0;
                char buffer[16*1024];
                while(totalBytesWritten < cb.QuadPart)
                {
                    unsigned long long remainingBytes = cb.QuadPart - totalBytesWritten;
                    unsigned long bytesToCopy = (unsigned long)min(UINT64(sizeof(buffer)),remainingBytes);

                    bytesRead = streamBase->Read(buffer,bytesToCopy);
                    

                    auto hr = pstm->Write(buffer,ULONG(bytesRead),&bytesWritten);

                    totalBytesRead += bytesRead;
                    totalBytesWritten += bytesWritten;
                    if(FAILED(hr))
                    {
                        return hr;
                    }

                    if((bytesRead == 0)||(bytesWritten == 0))
                    {
                        break;
                    }
                }
                if(pcbRead)
                {
                    pcbRead->QuadPart = totalBytesRead;
                }
                if(pcbWritten)
                {
                    pcbWritten->QuadPart = totalBytesWritten;
                }
            }
            catch(const std::exception& exception)
            {
                result = HRESULTFromException(exception);
            }
            catch(...)
            {
                result = E_FAIL;
            }
            return result;
        }
        HWIN_EXPORT HRESULT ComStream::Commit(DWORD grfCommitFlags)
        {
            HRESULT result = S_OK;
            try
            {
                auto streamBase = GetStreamBase();
                streamBase->Flush();
            }
            catch(const std::exception& exception)
            {
                result = HRESULTFromException(exception);
            }
            catch(...)
            {
                result = E_FAIL;
            }
            return result;
        }
        HWIN_EXPORT HRESULT ComStream::Revert( void )
        {
            HRESULT result = S_OK;
            return result;
        }
        HWIN_EXPORT HRESULT ComStream::LockRegion( ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD dwLockType)
        {
            HRESULT result = STG_E_INVALIDFUNCTION;
            return result;
        }
        HWIN_EXPORT HRESULT ComStream::UnlockRegion( ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD dwLockType)
        {
            HRESULT result = STG_E_INVALIDFUNCTION;
            return result;
        }
        HWIN_EXPORT HRESULT ComStream::Stat( STATSTG *pstatstg,DWORD grfStatFlag )
        {
            HRESULT result = S_OK;
            try
            {
                if(!pstatstg)
                {
                    return STG_E_INVALIDPOINTER;
                }
                
                auto streamBase = GetStreamBase();
                memset(pstatstg,0,sizeof(STATSTG));
                pstatstg->type = STGTY_STREAM;
                pstatstg->cbSize.QuadPart = streamBase->Size();
                GetSystemTimeAsFileTime(&pstatstg->mtime);
                pstatstg->ctime = pstatstg->mtime;
                pstatstg->atime = pstatstg->mtime;
            }
            catch(const std::exception& exception)
            {
                result = HRESULTFromException(exception);
            }
            catch(...)
            {
                result = E_FAIL;
            }
            return result;
        }

        HWIN_EXPORT HRESULT ComStream::Clone( IStream **ppstm )
        {
        HRESULT result = S_OK;
        try
        {
            if(!ppstm)
            {
                return STG_E_INVALIDPOINTER;
            }

            auto streamBase = GetStreamBase();
            auto clone = streamBase->Clone();

            result = clone->QueryInterface(IID_IStream,(void**)ppstm);
        }
        catch(const std::exception& exception)
        {
            result = HRESULTFromException(exception);
        }
        catch(...)
        {
            result = E_FAIL;
        }
        return result;
        }

    };
};

